Rails Remote Elements Tutorial
Do you need to create real-time features in your Rails app, but either can’t use Turbo or don’t want to use a front end framework like React? Fortunately older versions of Rails actually provide this functionality of the box. In this tutorial I’ll show you how to create a single page app in Rails from scratch using remote elements and Stimulus.
Formula
Stimulus Controller
What’s Going On Here?
- Rails-ujs dispatches custom events on the element creating the request. In our case we specifically listen for
ajax:error
andajax:success
. Those responses are returned viaevent.detail[2]
.- If the request was successful we simply update the DOM with the response according to the value of
this.actionValue
. We limit what actions can be used withthis.actionIsPermitted()
. These values are inspired by Turbo’s seven actions and are handled viathis.updateTarget()
.- Since a request can come from a button, link, or form we need to conditionally handle rendering errors and clearing form data via
this.clearErrors()
andthis.clearForm()
.
Remote Element Markup
Forms
What’s Going On Here?
- We need to add
local: false
to ensure the form will make an AJAX request.- The
request_target: "form"
data attribute ensures the form will be cleared viathis.clearForm()
.- The
request_target_value
data attribute references an element on the page that will be updated when the response from the server is successful.- The
request_action_value
data attribute determines how therequest_target_value
element will be updated when the response from the server is successful.- We add
<div data-request-target="error"></div>
so we can render any errors in the form if the object is not valid.
Buttons
What’s Going On Here?
- We need to add
remote: true
to ensure the form will make an AJAX request.- The
request_target_value
data attribute references an element on the page that will be updated when the response from the server is successful. Note that this is wrapped inform: { data: {} }
since we need this value to be set on the form and not the button.- The
request_action_value
data attribute determines how therequest_target_value
element will be updated when the response from the server is successful. Note that this is wrapped inform: { data: {} }
since we need this value to be set on the form and not the button.
Links
What’s Going On Here?
- We need to add
remote: true
to ensure the request will make an AJAX request.- The
request_target_value
data attribute references an element on the page that will be updated when the response from the server is successful.- The
request_action_value
data attribute determines how therequest_target_value
element will be updated when the response from the server is successful.
Controller Responses
When Responding With a Partial
What’s Going On Here?
- This action only returns partials instead of a full layout. If the
@task
is saved the server will respond withapp/views/tasks/_task.html.erb
. Otherwise it will respond withlayouts/_form_errors.html.erb
. In either case just the markup from the partial is returned instead of the full document.
When Responding With a Layout
What’s Going On Here?
- This action will return the full page layout (in this case
app/views/tasks/show.html.erb
) unless the request was an xhr request. If the request was made with xhr then only the content ofapp/views/tasks/show.html.erb
will be returned instead of the full document. We could have setlayout: false
but there could be a case where we actually visit the route (https://www.example.com/tasks/1). If we setlayout: false
then the response would be missing the actually page layout.
Example
Below is a real world example of how you can use remote elements to create a single page application in Rails.
Notes
- Since it’s a single page app, all requests are coming from the root path (
tasks#index
). - The back button won’t work as expected. For example, if you click on a task and then click the back button, you won’t be brought back to the
tasks#index
since you’re technically already there. Instead you’ll be brought back to whatever page you were on last. This is why there are client side routing libraries such as React Router. - In order to DRY up our code, we set the data attributes for some of the form partials in our controllers since we sometimes respond with a form partial. A good example of this is
tasks#edit
andapp/views/tasks/_form.html.erb
.